package de.justphil.tcg.tcgserver.rest.validators;

import java.io.UnsupportedEncodingException;

import javax.ws.rs.core.Response;

import org.slf4j.Logger;

import com.twmacinta.util.MD5;

import de.justphil.tcg.tcgserver.commons.config.RestConfig;
import de.justphil.tcg.tcgserver.commons.domain.CardInstance;
import de.justphil.tcg.tcgserver.commons.domain.User;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.FreebieCardReq;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.GeoLocation;
import de.justphil.tcg.tcgserver.commons.exceptions.TCGException;
import de.justphil.tcg.tcgserver.commons.geo.GeoCalculation;
import de.justphil.tcg.tcgserver.commons.i18n.Error;
import de.justphil.tcg.tcgserver.commons.management.DataManager;
import de.justphil.tcg.tcgserver.commons.util.StringUtil;
import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

public class FreebieCardValidator {
	
	private static final Logger log = restLogger(FreebieCardValidator.class);
	
	/*
	public void validatePost(FreebieCardReq req, double lat, double lon) throws TCGException {
		
		validateLatLon(lat, lon);
		
		validateHash(req, lat, lon);
		
	}
	*/
	
	public void validateGet(double lat, double lon, double radius) throws TCGException {
		
		validateLatLon(lat, lon);
		
		validateRadius(radius);
		
	}
	
	public CardInstance validatePut(DataManager dataManager, FreebieCardReq req, double lat, double lon, long ciId) throws TCGException {
		
		validateLatLon(lat, lon);
		
		validateHash(req, lat, lon, ciId);
		
		validateCardInstanceId(ciId);
		
		CardInstance ci = validateAvailability(dataManager, ciId);
		
		validateDistance( new GeoLocation( lat, lon ) , new GeoLocation( ci.getLatitude(), ci.getLongitude() ) );
		
		return ci;
	}
	
	private void validateCardInstanceId(long ciId) throws TCGException {
		
		if (ciId <= 0) {
			throw new TCGException(Error.CLIENT_ERROR_INVALID_CIID, Response.Status.BAD_REQUEST);
		}
		
	}
	
	private void validateDistance( GeoLocation g1, GeoLocation g2 ) throws TCGException {
		
		if ( new GeoCalculation().getDistance(g1, g2) > RestConfig.FREEBIE_RADIUS ) {
			throw new TCGException(Error.CLIENT_ERROR_TOO_FAR_AWAY, Response.Status.FORBIDDEN);
		}
		
	}
	
	private CardInstance validateAvailability(DataManager dataManager, long ciId) throws TCGException {
		
		CardInstance ci = dataManager.getCardInstanceById(ciId);
		
		if (ci.getUserId() != User.ManagerId.LOCATION) {
			throw new TCGException(Error.CLIENT_ERROR_CARD_INSTANCE_NOT_AVAILABLE, Response.Status.GONE);
		}
		
		return ci;
		
	}
	
	private void validateLatLon(double lat, double lon) throws TCGException {
		if (lat > 180.0 || lat < -180.0) {
			throw new TCGException(Error.CLIENT_ERROR_INVALID_LAT, Response.Status.BAD_REQUEST);
		}
		
		if (lon > 180.0 || lon < -180.0) {
			throw new TCGException(Error.CLIENT_ERROR_INVALID_LON, Response.Status.BAD_REQUEST);
		}
	}
	
	private void validateRadius(double radius) throws TCGException {
		if (radius < 0.0) {
			throw new TCGException(Error.CLIENT_ERROR_RADIUS_LESS_THAN_ZERO, Response.Status.BAD_REQUEST);
		}
		
		if (radius > RestConfig.GEOLOCATION_MAX_RADIUS) {
			throw new TCGException(Error.CLIENT_ERROR_RADIUS_GREATER_THAN_MAX, Response.Status.BAD_REQUEST);
		}
	}
	
	private void validateHash(FreebieCardReq req, double lat, double lon, long ciId) throws TCGException {
		if (req == null) {
			throw new TCGException(Error.CLIENT_ERROR_BAD_REQUEST, Response.Status.BAD_REQUEST);
		}
		
		if (StringUtil.isNullOrEmpty( req.getHash() )) {
			throw new TCGException(Error.CLIENT_ERROR_INVALID_HASH, Response.Status.FORBIDDEN);
		}
		
		StringBuilder sb = new StringBuilder(70);
		sb.append(lat).append(lon).append(ciId).append(RestConfig.FREEBIE_CARD_PRIVATE_KEY);
		String expectedHash = null;
		
		try {
			MD5 md5 = new MD5();
			md5.Update(sb.toString(), null);
			expectedHash = md5.asHex();
			log.debug("value => {}", sb.toString());
			log.debug("expectedHash => {}", expectedHash);
		}
		catch (UnsupportedEncodingException e) {
			throw new TCGException(Error.SERVER_ERROR_INTERNAL, Response.Status.INTERNAL_SERVER_ERROR);
		}
		
		if (expectedHash == null) {
			throw new TCGException(Error.SERVER_ERROR_INTERNAL, Response.Status.INTERNAL_SERVER_ERROR);
		}
		
		if (!expectedHash.equals( req.getHash() )) {
			throw new TCGException(Error.CLIENT_ERROR_INVALID_HASH, Response.Status.FORBIDDEN);
		}
	}

}
